package com.wyvoid.core.persistent.hibernate;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Query;
import org.hibernate.criterion.Criterion;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import java.io.Serializable;
import java.util.List;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;

import com.wyvoid.core.persistent.Pagination;


/**
 * @author cjp
 */
@SuppressWarnings({"unchecked"})
public abstract class HibernateDAO<T> extends HibernateDaoSupport implements IHibernateDAO<T> {
    protected final Log logger = LogFactory.getLog(getClass());

    /**
     * 返回此Dao所管理的Entity类型
     */
    private Class<T> poClass;

    public HibernateDAO() {
        poClass = (Class<T>) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0];

    }

    public T getById(Serializable id) {
        return (T) getHibernateTemplate().get(poClass, id);
    }

    public void delete(T po) {
        getHibernateTemplate().delete(po);
    }

    public void deleteById(Serializable id) {
        getHibernateTemplate().delete(getById(id));
    }

    public List<T> findAll() {
        //noinspection unchecked
        return getHibernateTemplate().find("from " + poClass.getName() + " obj");
    }

    public List<T> findByCriterion(final Criterion... criterion) {
        //noinspection unchecked
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException {
                Criteria criteria = session.createCriteria(poClass);
                for (Criterion c : criterion) {
                    criteria.add(c);
                }
                return criteria.list();
            }
        });
    }

    public void save(T po) {
        getHibernateTemplate().saveOrUpdate(po);
    }

    public void create(T po) {
        getHibernateTemplate().save(po);
    }

    public void update(T po) {
        getHibernateTemplate().update(po);
    }

    public HibernatePage<T> doQuery(String hql, boolean cacheable, int curPage, int pageSize, Object... values) {
        return new HibernatePage<T>(hql, getSessionFactory().getCurrentSession(), cacheable, curPage, pageSize, values);
    }

    public HibernatePage<T> doQuery(String hql, boolean cacheable, Pagination pagination, Object... values) {
        return new HibernatePage<T>(hql, getSessionFactory().getCurrentSession(), cacheable, pagination, values);
    }

    public T doQueryUnique(final String hql, final Object... values) {
        return (T) this.getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(hql);
                query.setCacheable(true);
                for (int i = 0; i < values.length; i++) {
                    query.setParameter(i, values[i]);
                }
                return  query.uniqueResult();
            }
        });

    }

    public T doQueryFirst(final String hql, final Object... values) {
        return (T) this.getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(hql);
                query.setCacheable(true);
                for (int i = 0; i < values.length; i++) {
                    query.setParameter(i, values[i]);
                }
                List list = query.list();
                if (list.size() > 0) {
                    return (T) list.get(0);
                } else {
                    return null;
                }
            }
        });
    }


    public void executeHsql(final String hql, final Object... values) {
        this.getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(hql);
                for (int i = 0; i < values.length; i++) {
                    query.setParameter(i, values[i]);
                }
                query.executeUpdate();
                return null;
            }
        }
        );
    }

    public List<T> doQueryList(final String hql, final boolean cacheable, final Object... values) {
        return (List<T>) this.getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(hql);
                query.setCacheable(cacheable);
                for (int i = 0; i < values.length; i++) {
                    query.setParameter(i, values[i]);
                }
                return query.list();
            }
        });
    }

    public String getFromHql() {
        return "from " + poClass;
    }

    public long doQueryCount(final String hql, final Object... values) {
        return (Long) this.getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(hql);
                for (int i = 0; i < values.length; i++) {
                    query.setParameter(i, values[i]);
                }
                Long count = (Long) query.uniqueResult();
                if (count == null) {
                    return 0;
                } else {
                    return count;
                }
            }
        });
    }

    public List<T> doQueryLimitList(
            final String hql,
            final boolean cacheable,
            final int dataNum,
            final Object... values) {
        return (List<T>) this.getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(hql);
                query.setCacheable(cacheable);
                for (int i = 0; i < values.length; i++) {
                    query.setParameter(i, values[i]);
                }
                query.setFirstResult(0);
                query.setMaxResults(dataNum);
                return query.list();
            }
        });
    }
}